/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
 * http://www.gnu.org/licenses/gpl-3.0.html
 *
 * $Revision: 11208 $
 * $Id: app.cpp 11208 2017-10-18 23:42:37Z fuscated $
 * $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-17.xx/src/src/app.cpp $
 */

#include <sdk.h>
#include "app.h"

#include <wx/arrstr.h>
#include <wx/fs_zip.h>
#include <wx/fs_mem.h>
#include <wx/xrc/xmlres.h>
#include <wx/cmdline.h>
#include <wx/regex.h>
#include <wx/filefn.h>
// for wxSafeShowMessage()
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <wx/choicdlg.h>
#include <wx/notebook.h>
#include <wx/clipbrd.h>
#include <wx/debugrpt.h>
#include <wx/ipc.h>
#include <wx/msgout.h>

#include <cbexception.h>
#include <configmanager.h>
#include <editormanager.h>
#include <globals.h>
#include <loggers.h>
#include <logmanager.h>
#include <manager.h>
#include <personalitymanager.h>
#include <pluginmanager.h>
#include <projectmanager.h>
#include <scriptingmanager.h>
#include <sdk_events.h>
#include <sqplus.h>

#include "appglobals.h"
#include "associations.h"
#include "cbauibook.h"
#include "cbstyledtextctrl.h"
#include "crashhandler.h"
#include "projectmanagerui.h"
#include "splashscreen.h"

#ifndef __WXMSW__
// binreloc
#include "prefix.h"
#endif

#if defined(__APPLE__) && defined(__MACH__)
#include <sys/param.h>
#include <mach-o/dyld.h>
#endif

#ifndef CB_PRECOMP
#include <wx/dir.h>
#include "xtra_res.h"
// LoaderBase
#include "filemanager.h"
#endif

#ifndef APP_PREFIX
#define APP_PREFIX ""
#endif

#ifdef __WXMSW__
// Crash handler DLL -> includes windows.h, therefore
#include "exchndl.h"
// ...include this header file on the NEXT LINE (wxWidgets docs say so)
#include <wx/msw/winundef.h>
#endif

#ifndef __WXMAC__
inline wxString GetResourcesDir(){ return wxEmptyString; }
#endif

namespace
{
    bool s_Loading = false;
/*客户/服务器的进程通信模式[DDE]IPC*/
/*DDE服务器端*/
    class DDEServer : public wxServer
    {
    public:
        DDEServer(MainFrame* frame) : m_Frame(frame) { ; }
        wxConnectionBase *OnAcceptConnection(const wxString& topic);
        MainFrame* GetFrame()                 { return m_Frame;  }
        void       SetFrame(MainFrame* frame) { m_Frame = frame; }
    private:
        MainFrame* m_Frame;
    };
/*DDE的TCP通信链接*/
    class DDEConnection : public wxConnection
    {
    public:
        DDEConnection(MainFrame* frame) : m_Frame(frame) { ; }
#if wxCHECK_VERSION(3, 0, 0)
        bool OnExecute(const wxString& topic, const void *data, size_t size, wxIPCFormat format);
#else
        bool OnExecute(const wxString& topic, wxChar *data, int size, wxIPCFormat format);
#endif
        bool OnDisconnect();
    private:
        MainFrame* m_Frame;
    };

/*DDE服务接受链接事件,事件参数是一个主题字符串*/
    wxConnectionBase* DDEServer::OnAcceptConnection(const wxString& topic)
    {
        return topic == DDE_TOPIC ? new DDEConnection(m_Frame) : nullptr;
    }

/*DDE链接执行事件*/
#if wxCHECK_VERSION(3, 0, 0)
    bool DDEConnection::OnExecute(cb_unused const wxString& topic, const void *data, cb_unused size_t size, cb_unused wxIPCFormat format)
#else
    bool DDEConnection::OnExecute(cb_unused const wxString& topic, wxChar *data, cb_unused int size, cb_unused wxIPCFormat format)
#endif
    {
        wxString strData((wxChar*)data);
        if (strData.StartsWith(_T("[IfExec_Open(\""))) return false;
        /*让Shell Open处理请求,因为我们已经注册了Shell Open命令*/
        if (strData.StartsWith(_T("[Open(\""))){
            /*打开指令*/
            wxRegEx reCmd(_T("\"(.*)\""));
            if (reCmd.Matches(strData))
            {
                const wxString file = reCmd.GetMatch(strData, 1);
                /*
                 总是将文件放在延迟队列中,将加载到OnDisconnect中,或者正在创建主框架[MainFrame]之后,
                 如果我们直接打开文件,则可能导致应用程序挂起(至少在linux上打开CodeBlocks的项目文件时如此)
                 */
                CodeBlocksApp* cb = (CodeBlocksApp*)wxTheApp;
                if (cb) cb->AddFileToOpenDelayed(file);
            }
            return true;
        }else if (strData.StartsWith(_T("[OpenLine(\""))){
            /*行打开指令,把文件压入*/
            wxRegEx reCmd(_T("\"(.*)\""));
            if (reCmd.Matches(strData))
            {
                wxString file = reCmd.GetMatch(strData, 1);
                CodeBlocksApp* cb = (CodeBlocksApp*)wxTheApp;
                cb->SetAutoFile(file);
            }
            return true;
        }else if (strData.StartsWith(_T("[Raise]"))){
            /*执行抛出异常指令 在主框架里抛出*/
            if (m_Frame)
            {
                if (m_Frame->IsIconized())
                    m_Frame->Iconize(false);
                m_Frame->Raise();
            }
            return true;
        }else if (strData.StartsWith(_T("[CmdLine({"))){
            /*执行命令行指令*/
            int pos = strData.Find(_T("})]"));
            if (pos!=wxNOT_FOUND)
            {
                wxString line = strData.Mid(10, pos-10);
                line.Replace(_T("\\)"), _T(")"));
                line.Replace(_T("\\("), _T("("));
                CodeBlocksApp* cb = (CodeBlocksApp*)wxTheApp;
                if (m_Frame && !line.empty())
                {
                    /*Manager::Get()->GetLogManager()->Log(wxString::Format(_T("DDEConnection::OnExecute line = ") + line));*/
                    cb->ParseCmdLine(m_Frame, line);
                    CodeBlocksEvent event(cbEVT_APP_CMDLINE);
                    Manager::Get()->ProcessEvent(event);
                }

            }
            return true;
        }
        wxSafeShowMessage(wxT("警告"),wxString::Format(wxT("未处理DDE主题%s."),strData.wx_str()));
        return false;
    }

    bool DDEConnection::OnDisconnect()
    {
        /*如果主框架[MainFrame]已经存在,则会自动加载延迟的文件,否则在创建主框架之后,会在OnInit中自动加载*/

        if (!s_Loading && m_Frame)
        {
            CodeBlocksApp* cb = (CodeBlocksApp*)wxTheApp;
            cb->LoadDelayedFiles(m_Frame);
        }
        return true;
    }

/*初始化全局DDE服务变量为空指针*/
    DDEServer* g_DDEServer = nullptr;
/*DDE服务客户端*/
    class DDEClient: public wxClient {
    public:
        DDEClient(void) {}
        wxConnectionBase *OnMakeConnection(void) { return new DDEConnection(nullptr); }
    };

#if wxUSE_CMDLINE_PARSER
#if wxCHECK_VERSION(3, 0, 0)
#define CMD_ENTRY(X) X
#else
#define CMD_ENTRY(X) _T(X)
#endif
/*DDE命令解释器,DDE命令数组,命令行功能描述*/
    const wxCmdLineEntryDesc cmdLineDesc[] =
            {
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("h"),  CMD_ENTRY("help"),                  CMD_ENTRY("显示帮助信息"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("?"),  CMD_ENTRY("?"),                     CMD_ENTRY("显示此帮助消息('别名帮助')"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("safe-mode"),             CMD_ENTRY("以安全模式加载(所有插件将被禁用)"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
#ifdef __WXMSW__
            { wxCMD_LINE_SWITCH, CMD_ENTRY("na"), CMD_ENTRY("no-check-associations"), CMD_ENTRY("don't perform any association checks"),
      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
    { wxCMD_LINE_SWITCH, CMD_ENTRY("nd"), CMD_ENTRY("no-dde"),                CMD_ENTRY("don't start a DDE server"),
      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
#endif
#ifndef __WXMSW__
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("ni"), CMD_ENTRY("no-ipc"),                CMD_ENTRY("不要启动IPC服务器"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
#endif
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("ns"), CMD_ENTRY("no-splash-screen"),      CMD_ENTRY("加载时不显示启动屏幕"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("multiple-instance"),     CMD_ENTRY("允许运行多个实例"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("d"),  CMD_ENTRY("debug-log"),             CMD_ENTRY("显示应用程序的调试日志"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("nc"), CMD_ENTRY("no-crash-handler"),      CMD_ENTRY("不要使用崩溃处理程序(对于调试C::B很有用)"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY("v"),  CMD_ENTRY("verbose"),               CMD_ENTRY("显示更多调试消息"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_OPTION, CMD_ENTRY(""),   CMD_ENTRY("prefix"),                CMD_ENTRY("共享数据目录前缀"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_OPTION, CMD_ENTRY(""),   CMD_ENTRY("user-data-dir"),         CMD_ENTRY("为用户设置和插件设置自定义位置"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_OPTION, CMD_ENTRY("p"),  CMD_ENTRY("personality"),           CMD_ENTRY("要使用的个性:'ask'或<个性名称>"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("no-log"),                CMD_ENTRY("关闭应用程序日志"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("log-to-file"),           CMD_ENTRY("将应用程序日志重定向到文件"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("debug-log-to-file"),     CMD_ENTRY("将应用程序调试日志重定向到文件"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_OPTION, CMD_ENTRY(""),   CMD_ENTRY("profile"),               CMD_ENTRY("个性的同义词"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("rebuild"),               CMD_ENTRY("清理并构建项目/工作区"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("build"),                 CMD_ENTRY("只需构建项目/工作区"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("clean"),                 CMD_ENTRY("清理项目/工作区"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_OPTION, CMD_ENTRY(""),   CMD_ENTRY("target"),                CMD_ENTRY("批处理生成的目标"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("no-batch-window-close"), CMD_ENTRY("批生成完成时不自动关闭日志窗口"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_SWITCH, CMD_ENTRY(""),   CMD_ENTRY("batch-build-notify"),    CMD_ENTRY("批生成完成时显示消息"),
                      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
                    { wxCMD_LINE_OPTION, CMD_ENTRY(""),   CMD_ENTRY("script"),                CMD_ENTRY("执行脚本文件"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_OPTION, CMD_ENTRY(""),   CMD_ENTRY("file"),                  CMD_ENTRY("打开文件并选择跳转到特定行(文件[:行])"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR },
                    { wxCMD_LINE_PARAM,  CMD_ENTRY(""),   CMD_ENTRY(""),                      CMD_ENTRY("文件名"),
                      wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE },
                    { wxCMD_LINE_NONE }
            };
#endif
// wxUSE_CMDLINE_PARSER

/*启动界面类*/
    class Splash
    {
    public:
        Splash(const bool show) : m_pSplash(nullptr)
        {
            if (show)
            {
                wxBitmap bmp = cbLoadBitmap(ConfigManager::ReadDataPath() + _T("/images/splash_1312.png"));
                m_pSplash = new cbSplashScreen(bmp, -1, nullptr, -1, wxNO_BORDER | wxFRAME_NO_TASKBAR | wxFRAME_SHAPED);
                Manager::Yield();
            }
        }
        ~Splash()
        {
            Hide();
        }
        void Hide()
        {
            if (m_pSplash)
            {
                m_pSplash->Destroy();
                m_pSplash = nullptr;
            }
        }
    private:
        cbSplashScreen* m_pSplash;
    };

    class cbMessageOutputNull : public wxMessageOutput
    {
    public:

#if wxCHECK_VERSION(3, 0, 0)
        virtual void Output(const wxString &str);
#else
        #ifdef WX_ATTRIBUTE_PRINTF
    virtual void Printf(const wxChar* format, ...)  WX_ATTRIBUTE_PRINTF_2;
    #else
    virtual void Printf(const wxChar* format, ...)  ATTRIBUTE_PRINTF_2;
    #endif
#endif
// wxCHECK_VERSION
    };
#if wxCHECK_VERSION(3, 0, 0)
    void cbMessageOutputNull::Output(cb_unused const wxString &str){}
#else
    void cbMessageOutputNull::Printf(cb_unused const wxChar* format, ...){}
#endif
}

/*程序入口*/
IMPLEMENT_APP(CodeBlocksApp)
/*TODO: This gives a "redundant declaration" warning, though I think it's false. Dig through macro and check.*/
/*TODO: 这给出了一个"多余声明"警告,尽管我认为这是错误的.钻研宏并检查.*/
BEGIN_EVENT_TABLE(CodeBlocksApp, wxApp)
                EVT_ACTIVATE_APP(CodeBlocksApp::OnAppActivate)
                EVT_TASKBAR_LEFT_DOWN(CodeBlocksApp::OnTBIconLeftDown)
END_EVENT_TABLE()

#ifdef __WXMAC__
#if wxCHECK_VERSION(3, 0, 0)
#include "wx/osx/core/cfstring.h"
#else
#include "wx/mac/corefoundation/cfstring.h"
#endif
#include "wx/intl.h"

#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFURL.h>

/*returns e.g. "/Applications/appname.app/Contents/Resources" if application is bundled, or the directory of the binary, e.g. "/usr/local/bin/appname", if it is *not* bundled.*/
/*如果应用程序已绑定,则返回“/Applications/appname.app/Contents/Resources”,如果二进制文件的目录未绑定,则返回“/usr/local/bin/appname”.*/
static wxString GetResourcesDir()
{
    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
    /*relative -> absolute 相对到绝对*/
    CFURLRef absoluteURL = CFURLCopyAbsoluteURL(resourcesURL);
    CFRelease(resourcesURL);
    CFStringRef cfStrPath = CFURLCopyFileSystemPath(absoluteURL,kCFURLPOSIXPathStyle);
    CFRelease(absoluteURL);
    #if wxCHECK_VERSION(3, 0, 0)
      return wxCFStringRef(cfStrPath).AsString(wxLocale::GetSystemEncoding());
    #else
      return wxMacCFStringHolder(cfStrPath).AsString(wxLocale::GetSystemEncoding());
    #endif
}
#endif

/*载入配置*/
bool CodeBlocksApp::LoadConfig()
{
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.1开始载入配置..."));
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.2如果存在用户数据目录并设置"));
    if (m_UserDataDir!=wxEmptyString)
    {
        wxString dmsg;
        dmsg.Printf(wxT("--4.2.1用户数据目录:%s"),m_UserDataDir.wx_str());
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8(dmsg));

        /*if --user-data-dir=path was specified we tell ConfigManager (and CfgMgrBldr) about it, which will propagate it through the app and plugins*/
        /*如果指定了--user-data-dir=path,我们将告诉ConfigManager(和CfgMgrBldr),后者将通过应用程序和插件传播它*/

        if ( !ConfigManager::SetUserDataFolder(m_UserDataDir) ) return false;

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.2.2用户数据目录设置成功"));
    }

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.3用户数据目录存在并设置成功或者不存"));

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.4获取配置数据"));
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));

    wxString data(wxT(APP_PREFIX));

    if (platform::windows) {
        data.assign(GetAppPath());
    }else if (platform::macosx){
        data.assign(GetResourcesDir());
        /*CodeBlocks.app/Contents/Resources not a bundle, use relative path*/
        /*不是捆绑包,使用相对路径*/
        if (!data.Contains(wxString(_T("/Resources")))) data = GetAppPath() + _T("/..");
    }

    if (data.IsEmpty()){
        wxString dmsg1;
        dmsg1.Printf(wxT("--4.4.1配置数据为空情况构造为目录:%s"),GetAppPath().wx_str());
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8(dmsg1));
        /*fallback 回退*/
        data.assign(GetAppPath());
        data.Replace(_T("/bin"),_T(""));
    }

    /*--prefix command line switch overrides builtin value*/
    /*--prefix命令行开关覆盖内置值*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.5[--prefix]命令行开关覆盖内置值,同时,检查环境"));
    if (!m_Prefix.IsEmpty()){
        data = m_Prefix;
    }else{
        /*also, check for environment*/
        /*同时,检查环境*/
        wxString env;
        wxGetEnv(_T("CODEBLOCKS_DATA_DIR"), &env);
        if (!env.IsEmpty()){
            data = env;
        }
    }

    /*添加并写入指向共享数据的目录*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.6添加并写入指向共享数据的目录"));
    data.append(_T("/share/codeblocks"));
    cfg->Write(_T("data_path"), data);

    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("--4.7配置数据:%s"), data.wx_str()));

    return true;
}

/*初始化文件关联*/
void CodeBlocksApp::InitAssociations()
{
#ifdef __WXMSW__
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
    if (m_Assocs && cfg->ReadBool(_T("/environment/check_associations"), true))
    {
        if (!Associations::Check())
        {
            AskAssocDialog dlg(Manager::Get()->GetAppWindow());
            PlaceWindow(&dlg);

            switch(dlg.ShowModal())
            {
            case ASC_ASSOC_DLG_NO_DONT_ASK:
                cfg->Write(_T("/environment/check_associations"), false);
                break;
            case ASC_ASSOC_DLG_NO_ONLY_NOW:
                break;
            case ASC_ASSOC_DLG_YES_C_FILES:
                Associations::SetCore();
                break;
            case ASC_ASSOC_DLG_YES_ALL_FILES:
                Associations::SetAll();
                break;
            default:
                break;
            };
        }
    }
#endif
}

/*初始化调试控制台*/
void CodeBlocksApp::InitDebugConsole()
{
#ifdef __WXMSW__
    #ifdef __CBDEBUG__
    /*Remember to compile as a console application!*/
    /*记住编译为控制台应用程序！*/
    AllocConsole();
    HANDLE myhandle = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD co = {80,2000};
    SetConsoleScreenBufferSize(myhandle, co);
    fprintf(stdout,"CONSOLE DEBUG ACTIVATED\n");
    /*wxLogWindow *myerr = new wxLogWindow(NULL,"debug");*/
    #endif
#endif
}

/*初始化异常处理程序*/
void CodeBlocksApp::InitExceptionHandler()
{
#ifdef __WXMSW__
    ExcHndlInit();
#endif
}

/*初始化资源缓冲槽*/
bool CodeBlocksApp::InitXRCStuff()
{
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--6.1初始化xRC..."));
    wxString env;
    wxGetEnv(_T("CODEBLOCKS_DATA_DIR"), &env);

    if (env.IsEmpty()){
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--6.1a空数据目录..."));
    }else{
        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("--6.1b数据目录:%s"),env.wx_str()));
    }
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--6.2资源载入中..."));
    if ( !Manager::LoadResource("resources.zip") )
    {
        wxString msg;
        msg.Printf(wxT("--6.2a找不到资源... \n %s 已配置为安装在'%s'中.\n请使用命令行开关'--prefix',或将CODEBLOCKS_DATA_DIR环境变量设置为指向安装%s的位置,\n或尝试重新安装应用程序..."),
                   appglobals::AppName.wx_str(),
                   ConfigManager::ReadDataPath().wx_str(),
                   appglobals::AppName.wx_str());
        Manager::Get()->GetLogManager()->DebugLog(msg);
        /*cbMessageBox(msg);*/
        return false;
    }
    return true;
}

/*初始化主框架*/
MainFrame* CodeBlocksApp::InitFrame()
{
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.0初始化主框架-检测wxWidgets的版本要求..."));
    /*
     * static_assert编译期断言
     * static_assert ( 布尔常量表达式 , 消息 )		(C++11 起)
     * static_assert ( 布尔常量表达式 )		(C++17 起)
     * assert运行期断言
     * */

    static_assert(wxMinimumVersion<2,8,12>::eval, "wxWidgets版本必须大于等于2.8.12");

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.1创建主框架..."));
    MainFrame *frame = new MainFrame();

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.2设置刷新界面时间片100毫秒并设置主框架为最顶层窗口..."));
    wxUpdateUIEvent::SetUpdateInterval(100);
    /*来自于wxWidgets的app.h继承于wxAppBase*/
    SetTopWindow(nullptr);
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.3在DDE服务器中设置并返回主框架..."));
    /*Set m_Frame in DDE-Server*/
    /*在DDE服务器中设置m_Frame*/
    if (g_DDEServer && m_DDE)  g_DDEServer->SetFrame(frame);

    return frame;
}

/*检查版本*/
void CodeBlocksApp::CheckVersion()
{
    /*This is a remnant from early 2006 (Windows only), but keep the revision tag for possible future use*/
    /*这是2006年初遗留下来的(仅限Windows),但请保留修订标签以备将来使用*/
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));

    if (cfg->Read(_T("version")) != appglobals::AppActualVersion)
        cfg->Write(_T("version"), appglobals::AppActualVersion);
}

/*初始化本地化语言*/
void CodeBlocksApp::InitLocale() {
    /*定义应用程序的配置管理器*/
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
    /*本地化语言包存放目录(对象化字符串)*/
    wxString path(ConfigManager::GetDataFolder() + _T("/locale"));
    /*日志管理器调试输出日志*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("--7.1本地化语言文件存放目录:%s"),path.wx_str()));
    /*不存在可用的本地语言则初始化为[中文简体]*/
    if (cfg->ReadBool(_T("/locale/enable"), false) == false) {
        m_locale.Init(wxLocale::GetLanguageInfo(wxLANGUAGE_CHINESE_SIMPLIFIED)->Language);
        return;
    }
    /*读取配置记录:本地化语言包名称(对象化字符串)*/
    wxString lang(cfg->Read(wxT("/locale/language")));
    /*向本地化处理对象添加本地化语言包搜索路径*/
    wxLocale::AddCatalogLookupPathPrefix(path);
    /*定义本地化语言包对象(指针)*/
    const wxLanguageInfo *info;
    /*Note: You can also write this line of code as !(!lang) from wx-2.9 onwards*/
    /*注意:你也可以写这行代码为!(!lang)从wx-2.9开始*/
    if (!lang.IsEmpty()) {
        Manager::Get()->GetLogManager()->DebugLog(wxT("--7.1.1存在本地化语言文件"));
        info = wxLocale::FindLanguageInfo(lang);
    } else {
        Manager::Get()->GetLogManager()->DebugLog(wxT("--7.1.2不存在本地化语言文件[初始化为中文简体]"));
        /*info = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);*/
        info = wxLocale::GetLanguageInfo(wxLANGUAGE_CHINESE_SIMPLIFIED);
    }

    /*should never happen, but who knows...*/
    /*无效的语言包信息,不会发生的,但谁知道...*/
    if (info == nullptr) {
        return;
    }
    /*初始化本地语言*/
    m_locale.Init(info->Language);
    /*分配内存给语言包存储目录字符串,并构造全路径字符串*/
    path.Alloc(path.length() + 10);
    path.Append(_T('/'));
    path.Append(info->CanonicalName);

    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("--7.2语言文件:%s"),path.wx_str()));

    if ( !wxDirExists(path) ) {
        Manager::Get()->GetLogManager()->DebugLog(F(wxT("--7.3语言文件目录[%s]不存在"),path.wx_str()));
        return;
    }

    wxDir dir(path);
    if (!dir.IsOpened()) {
        Manager::Get()->GetLogManager()->DebugLog(F(wxT("--7.4打不开语言文件目录:%s"),path.wx_str()));
        return;
    }

    wxString moName;

    if (dir.GetFirst(&moName, _T("*.mo"), wxDIR_FILES))
    {
        do
        {
            m_locale.AddCatalog(moName);
        } while (dir.GetNext(&moName));
    }
}

/*初始化应用*/
bool CodeBlocksApp::OnInit()
{
    /*Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--1.初始化应用程序[设置程序名|初始化全局变量|刷新粘帖版|创建命令行解释器]..."));*/
    Manager::Get()->GetLogManager()->Log(wxString::FromUTF8("--1.初始化应用程序[设置程序名|初始化全局变量|刷新粘帖版|创建命令行解释器]..."));
#ifdef __WXMSW__
    InitCommonControls();
#endif

    wxLog::EnableLogging(true);
    SetAppName(wxT("codeblocks"));

    s_Loading              = true;
    m_pBatchBuildDialog    = nullptr;
    m_BatchExitCode        = 0;
    m_Batch                = false;
    m_BatchNotify          = false;
    m_Build                = false;
    m_ReBuild              = false;
    m_Clean                = false;
    m_HasProject           = false;
    m_HasWorkSpace         = false;
    m_SafeMode             = false;
    m_BatchWindowAutoClose = true;

    wxTheClipboard->Flush();

    wxCmdLineParser& parser = *Manager::GetCmdLineParser();
    parser.SetDesc(cmdLineDesc);

    /*NOTE: crash handler explicitly disabled because it causes problems with plugins loading/unloading...*/
    /*注意:崩溃处理程序显式禁用,因为它导致插件加载/卸载问题...*/
    /*static CrashHandler crash_handler(!m_CrashHandler);*/
    /*we'll do this once and for all at startup*/
    /*我们会在启动时创建系统处理程序一劳永逸*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--2.创建资源处理器[压缩/内存文件系统处理程序|提取工具条/滚动对话框XML资源|图像文件处理程序]..."));

    wxFileSystem::AddHandler(new wxZipFSHandler);
    wxFileSystem::AddHandler(new wxMemoryFSHandler);
    wxXmlResource::Get()->InsertHandler(new wxToolBarAddOnXmlHandler);
    wxXmlResource::Get()->InsertHandler(new wxScrollingDialogXmlHandler);
    /*初始化所有图像处理程序*/
    wxInitAllImageHandlers();
    wxXmlResource::Get()->InitAllHandlers();

    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("--3.1.启动") + appglobals::AppName + wxT(" ") +appglobals::AppActualVersionVerb + wxT(" ") +appglobals::AppBuildTimestamp));
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--3.2.启动异常陷阱[try]处理程序..."));
    try
    {

#if (wxUSE_ON_FATAL_EXCEPTION == 1)
        wxHandleFatalExceptions(true);
#endif
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--3.3.初始化异常处理程序..."));
        InitExceptionHandler();

        /*No output. (suppress warnings about unknown options from plugins)*/
        /*没有输出.(禁止显示插件中有关未知选项的警告)*/
        delete wxMessageOutput::Set(new cbMessageOutputNull);
        /*only abort if '--help' was passed in the command line*/
        /*仅当在命令行中传递"-help"时中止*/

        if (ParseCmdLine(nullptr) == -1)
        {
            delete wxMessageOutput::Set(new wxMessageOutputMessageBox);
            parser.Usage();

            return false;
        }

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--4.载入配置|载入异常退出..."));
        if ( !LoadConfig() ) return false;

        /*set safe-mode appropriately*/
        /*适当设置安全模式*/
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--5.插件管理器设置为安全模式..."));
        PluginManager::SetSafeMode(m_SafeMode);

        /*If not in batch mode, and no startup-script defined, initialise XRC*/
        /*如果不在批处理模式下,并且没有定义启动脚本,初始化XRC资源*/
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--6.初始化XRC资源..."));
        if(!m_Batch && m_Script.IsEmpty() && !InitXRCStuff()) return false;

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--7.初始化本地化语言列表..."));
        /*初始化本地语言列表*/
        InitLocale();

        /*处理用户DDE通讯*/
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--8.处理用户DDE通讯..."));

        if (m_DDE && !m_Batch && Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/use_ipc"), true))
        {
            /*Create a new client*/
            /*创建一个新的DDE客户端*/
            DDEClient *client = new DDEClient;
            DDEConnection* connection = nullptr;
            /*own error checking implemented -> avoid debug warnings*/
            /*已实现自己的错误检查->避免调试警告*/
            wxLogNull ln;
            connection = (DDEConnection *)client->MakeConnection(_T("localhost"), F(DDE_SERVICE, wxGetUserId().wx_str()), DDE_TOPIC);

            if (connection)
            {
                /*don't eval here just forward the whole command line to the other instance*/
                /*不要在这里执行eval只需将整个命令行转发到另一个实例*/
                wxString cmdLine;
                for (int i = 1 ; i < argc; ++i)
                    cmdLine += wxString(argv[i]) + _T(' ');

                if ( !cmdLine.IsEmpty() )
                {
                    /*escape openings and closings so it is easily possible to find the end on the rx side*/
                    /*逃出开启和关闭,以便很容易在rx侧找到末端*/
                    cmdLine.Replace(_T("("), _T("\\("));
                    cmdLine.Replace(_T(")"), _T("\\)"));
                    connection->Execute(_T("[CmdLine({") + cmdLine + _T("})]"));
                }

                /*On Linux, C::B has to be raised explicitely if it's wanted*/
                /*在Linux上,如果需要的话,CodeBlocks必须被明确地抛出异常*/
                if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/raise_via_ipc"), true))
                    connection->Execute(_T("[Raise]"));
                connection->Disconnect();
                delete connection;
                delete client;

                /*return false to end the application*/
                /*返回false去结束应用程序*/
                return false;
            }
            /*free memory DDE-/IPC-clients, if we are here connection could not be established and there is no need to free it*/
            /*空闲内存DDE-/IPC客户端,如果我们在这里连接无法建立,也不需要释放它*/
            delete client;
        }
        /*Now we can start the DDE-/IPC-Server, if we did it earlier we would connect to ourselves*/
        /*现在我们可以启动DDE-/IPC服务器了,如果我们早点启动的话,我们会自己连接*/
        if (m_DDE && !m_Batch)
        {
            g_DDEServer = new DDEServer(nullptr);
            g_DDEServer->Create(F(DDE_SERVICE, wxGetUserId().wx_str()));
        }

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--9.单实例/多实例..."));
        m_pSingleInstance = nullptr;
        if (   Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/single_instance"), true)
               && !parser.Found(_T("multiple-instance")) )
        {
            const wxString name = wxString::Format(wxT("Code::Blocks-%s"), wxGetUserId().wx_str());

            m_pSingleInstance = new wxSingleInstanceChecker(name, ConfigManager::GetTempFolder());
            if (m_pSingleInstance->IsAnotherRunning())
            {
                /*
                 * NOTE: Due to a recent change in logging code, this visual warning got disabled.
                 * So the wxLogError() has been changed to a cbMessageBox().
                 * 注意:由于最近日志代码的更改,此可见警告被禁用.因此wxLogError()已更改为cbMessageBox().
                 */

                cbMessageBox(wxT("另一个程序实例已在运行.\nCodeBlocks当前配置为只允许一个正在运行的实例.\n您可以在'环境[Environment]'菜单项下访问此设置."),wxT("Code::Blocks"), wxOK | wxICON_ERROR);
                return false;
            }
        }

        /*启动屏幕*/
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--10.启动屏幕..."));
/*
        Splash screen moved to this place, otherwise it would be short visible, even if we only pass filenames via DDE/IPC
        we also don't need it, if only a single instance is allowed
        启动屏幕移到这个位置,否则它可见时间将是很短,即使我们只通过DDE/IPC传递文件名.如果只允许一个实例,我们也不需要它
*/
        Splash splash(!m_Batch && m_Script.IsEmpty() && m_Splash && Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/show_splash"), true));

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--11.初始化调试控制台..."));
        /*初始化调试控制台*/
        InitDebugConsole();
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--12.设置批处理脚本管理..."));
        /*设置批处理脚本管理*/
        Manager::SetBatchBuild(m_Batch || !m_Script.IsEmpty());
        Manager::Get()->GetScriptingManager();

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--13.创建主框架并初始化主框架..."));
        /*创建主框架并初始化主框架*/
        MainFrame* frame = nullptr;
        frame = InitFrame();
        m_Frame = frame;

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--14.主框架初始化成功,插件已经被装载再次检测命令行参数..."));

        /*plugins loaded -> check command line arguments again warn about unknown options*/
        /*加载的插件->再次检查命令行参数,警告未知选项*/
        delete wxMessageOutput::Set(new wxMessageOutputBest);

        if ( ParseCmdLine(m_Frame) == 0 )
        {
            /*存在非空工作区,载入工作区*/
            if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/blank_workspace"), true) == false)
                Manager::Get()->GetProjectManager()->LoadWorkspace();
        }

        /*re-enable logging in safe-mode*/
        /*在安全模式下重新启用日志记录*/
        if (m_SafeMode) wxLog::EnableLogging(true);

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--15.如果设置为批处理,向主框架置入延时执行文件..."));
        if (m_Batch)
        {
            /*设置应用已启动标记*/
            Manager::SetAppStartedUp(true);

            /*the compiler plugin might be waiting for this*/
            /*编译器插件可能正在等待,触发已启动处理事件*/
            CodeBlocksEvent event(cbEVT_APP_STARTUP_DONE);
            Manager::Get()->ProcessEvent(event);
            /*注册编译器插件完成事件接收器[绑定批构建完成消息]*/
            Manager::Get()->RegisterEventSink(cbEVT_COMPILER_FINISHED, new cbEventFunctor<CodeBlocksApp, CodeBlocksEvent>(this, &CodeBlocksApp::OnBatchBuildDone));
            s_Loading = false;
            /*载入延时文件到主框架*/
            LoadDelayedFiles(frame);
            /*执行批任务*/
            BatchJob();
            frame->Close();
            return true;
        }
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--16.不存在批处理,检测脚本,如果脚本非空则执行载入脚本..."));
        /*不存在批处理,检测脚本*/
        if (!m_Script.IsEmpty())
        {
            /*执行脚本非空,载入脚本*/
            s_Loading = false;
            LoaderBase* loader = Manager::Get()->GetFileManager()->Load(m_Script);
            /*载入脚本到缓冲区*/
            if (loader->GetData()) Manager::Get()->GetScriptingManager()->LoadBuffer(cbC2U(loader->GetData()));
            delete loader;
            frame->Close();
            return true;
        }
        /*脚本为空*/

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--17.脚本为空,检测版本..."));
        CheckVersion();

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--18.尝试运行启动脚本..."));
        /*run startup script*/
        /*运行启动脚本*/
        try
        {
            wxString startup = ConfigManager::LocateDataFile(_T("startup.script"), sdScriptsUser | sdScriptsGlobal);
            if (!startup.IsEmpty())
                Manager::Get()->GetScriptingManager()->LoadScript(startup);
        }
        catch (SquirrelError& exception)
        {
            Manager::Get()->GetScriptingManager()->DisplayErrors(&exception);
        }

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--19.管理器执行待处理事件..."));
        Manager::ProcessPendingEvents();

        /*最后,显示应用程序*/
        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--20.隐藏启动屏幕..."));
        splash.Hide();

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--21.主框架前置并显示..."));
        SetTopWindow(frame);
        frame->Show();

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--21.主框架启动完成,显示提示..."));
        frame->StartupDone();
        /*this func checks if the user wants tips, so no need to check here*/
        /*此函数检查用户是否需要提示,因此无需在此处检查*/
        frame->ShowTips();

        /*如果是windows平台初始化文件关联*/
        if (platform::windows){ InitAssociations();}

        s_Loading = false;

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--22.在主框架载入延时处理文件..."));
        LoadDelayedFiles(frame);

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--23.项目管理器发送工作区被改变消息..."));
        Manager::Get()->GetProjectManager()->WorkspaceChanged();

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--24.设置应用已经全部启动完成标记并响应该事件..."));
        /*全部完成*/
        Manager::SetAppStartedUp(true);

        CodeBlocksEvent event(cbEVT_APP_STARTUP_DONE);
        Manager::Get()->ProcessEvent(event);

        Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--25.至此成功..."));

        //Manager::Get()->GetLogManager()->Log(Manager::Get()->GetConfigManager(_T("app"))->GetUserDataFolder());
        //Manager::Get()->GetLogManager()->Log(Manager::Get()->GetConfigManager(_T("app"))->GetConfigFolder());

        return true;
    }
    catch (cbException& exception)
    {
        exception.ShowErrorMessage();
    }
    catch (SquirrelError& exception)
    {
        Manager::Get()->GetScriptingManager()->DisplayErrors(&exception);
    }
    catch (const char* message)
    {
        wxSafeShowMessage(wxT("程序启动异常"), cbC2U(message));
    }
    catch (...)
    {
        wxSafeShowMessage(wxT("程序启动异常"), wxT("引发了未知异常,申请将立即终止..."));
    }
    /*if we reached here, return error*/
    /*如果我们到达这里,返回错误*/
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--26.程序初始化异常,启动失败..."));
    return false;
}

/*退出应用*/
int CodeBlocksApp::OnExit()
{
    wxTheClipboard->Flush();

    if (g_DDEServer){delete g_DDEServer;}

    if (m_pSingleInstance){delete m_pSingleInstance;}

    /*ultimate shutdown...*/
    /*最终关闭*/
    Manager::Free();

    /*WX docs say that this function's return value is ignored,but we return our value anyway. It might not be ignored at some point...*/
    /*WX文档说这个函数的返回值被忽略,但是我们仍然返回我们的值.在某个时候它可能不会被忽略...*/
    return m_Batch ? m_BatchExitCode : 0;
}

#ifdef __WXMSW__
inline void EnableLFH()
    {
        typedef BOOL  (WINAPI *HeapSetInformation_t)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
        typedef DWORD (WINAPI *GetProcessHeaps_t)(DWORD, PHANDLE);

        HINSTANCE kh = GetModuleHandle(TEXT("kernel32.dll"));
        HeapSetInformation_t  HeapSetInformation_func = (HeapSetInformation_t)  GetProcAddress(kh, "HeapSetInformation");
        GetProcessHeaps_t     GetProcessHeaps_func    = (GetProcessHeaps_t)     GetProcAddress(kh, "GetProcessHeaps");

        if (GetProcessHeaps_func && HeapSetInformation_func)
        {
            ULONG  HeapFragValue = 2;

            int n = GetProcessHeaps_func(0, 0);
            HANDLE *h = new HANDLE[n];
            GetProcessHeaps_func(n, h);

            for (int i = 0; i < n; ++i)
                HeapSetInformation_func(h[i], HeapCompatibilityInformation, &HeapFragValue, sizeof(HeapFragValue));

            delete[] h;
        }
    }
#else
inline void EnableLFH() {}
#endif

/*运行应用响应[启用消息循环?]*/

int CodeBlocksApp::OnRun()
{
    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--OnRun..."));
    EnableLFH();
    try
    {
        /*执行父类的OnRun*/
        int retval = wxApp::OnRun();
        /*wx 2.6.3 docs says that OnRun() function's return value is used as exit code*/
        /*wx 2.6.3文档中说OnRun()函数的返回值用作退出代码*/
        return m_Batch ? m_BatchExitCode : retval;
    }
    catch (cbException& exception)
    {
        exception.ShowErrorMessage();
    }
    catch (SquirrelError& exception)
    {
        Manager::Get()->GetScriptingManager()->DisplayErrors(&exception);
    }
    catch (const char* message)
    {
        wxSafeShowMessage(wxT("异常"), cbC2U(message));
    }
    catch (...)
    {
        wxSafeShowMessage(wxT("异常"), wxT("引发了未知异常,应用程序将立即终止..."));
    }
    /*如果我们到达这里,返回错误*/
    return -1;
}

/*响应命令行解释器事件*/
bool CodeBlocksApp::OnCmdLineParsed(wxCmdLineParser& parser)
{
    return wxApp::OnCmdLineParsed(parser);
}

void CodeBlocksApp::OnFatalException()
{
#if wxUSE_DEBUGREPORT && wxUSE_XML && wxUSE_ON_FATAL_EXCEPTION
    wxDebugReport report;
    wxDebugReportPreviewStd preview;

    report.AddAll();
    if ( preview.Show(report) ){report.Process();}
#else
    cbMessageBox(wxString::Format(wxT("%s内部出现问题,将立即终止.对于给您带来的不便,我们深表歉意..."), appglobals::AppName.wx_str()));
#endif
}

/*编译任务批处理*/
int CodeBlocksApp::BatchJob()
{
    if (!m_Batch){return -1;}
    /*查找编译器插件*/
    cbCompilerPlugin *compiler = Manager::Get()->GetPluginManager()->GetFirstCompiler();
    if (!compiler){return -3;}
    /*如果清理而且批处理目标的方式是征询方式*/
    if (!m_Clean && m_BatchTarget.Lower() == _T("ask"))
    {
        m_BatchTarget.Clear();
        cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (prj)
        {
            int idx = -1;
            wxString defTarget = prj->GetActiveBuildTarget();
            /*查找激活态的目标索引*/
            /*TODO: 在SDK中简化此操作*/
            if(!defTarget.IsEmpty()) {
                for (int i = 0; i < prj->GetBuildTargetsCount(); ++i) {
                    ProjectBuildTarget *target = prj->GetBuildTarget(i);
                    if (target->GetTitle().Matches(defTarget)) {
                        idx = i;
                        break;
                    }
                }
                idx = prj->SelectTarget(idx, false);
            }
            /*未选择目标:仅中止*/
            if (idx == -1){return 0;}
            m_BatchTarget = prj->GetBuildTarget(idx)->GetTitle();
        }
    }

    /*编译任务批处理对话框*/
    m_pBatchBuildDialog = m_Frame->GetBatchBuildDialog();
    PlaceWindow(m_pBatchBuildDialog);

    wxString title =F(wxT("开始构建 '%s'(目标'%s')"),wxFileNameFromPath(wxString(argv[argc-1])).wx_str() ,m_BatchTarget.wx_str());
    wxTaskBarIcon* tbIcon = new wxTaskBarIcon();
    tbIcon->SetIcon(
#ifdef __WXMSW__
            wxICON(A_MAIN_ICON),
#else
            wxIcon(app),
#endif
            title);

    wxString bb_title = m_pBatchBuildDialog->GetTitle();
    m_pBatchBuildDialog->SetTitle(bb_title + _T(" - ") + title);
    m_pBatchBuildDialog->Show();

    /*重新构建*/
    if (m_ReBuild)
    {
        if (m_HasProject) {
            compiler->Rebuild(m_BatchTarget);
        }else if (m_HasWorkSpace) {
            compiler->RebuildWorkspace(m_BatchTarget);
        }
    }else if (m_Build){
        if (m_HasProject) {
            compiler->Build(m_BatchTarget);
        }else if (m_HasWorkSpace) {
            compiler->BuildWorkspace(m_BatchTarget);
        }
    }else if (m_Clean){
        if (m_HasProject) {
            compiler->Clean(m_BatchTarget);
        }else if (m_HasWorkSpace) {
            compiler->CleanWorkspace(m_BatchTarget);
        }
    }

    /*批生成日志可能已在CodeBlocksApp::OnBatchBuildDone()中删除.*/
    /*如果没有,它仍然在编译.*/
    if (m_pBatchBuildDialog)
    {
        /*如果操作是"--clean",则无需显示对话框,因为该操作是同步的,并且在返回对clean()的调用时已完成.*/
        if (!m_Clean) {
            m_pBatchBuildDialog->ShowModal();
        }

        if ( m_pBatchBuildDialog->IsModal() ) {
            m_pBatchBuildDialog->EndModal(wxID_OK);
        }else{
            m_pBatchBuildDialog->Destroy();
            m_pBatchBuildDialog = nullptr;
        }
    }

    if (tbIcon)
    {
        tbIcon->RemoveIcon();
        delete tbIcon;
    }

    return 0;
}

/*响应编译任务批处理完成事件*/
void CodeBlocksApp::OnBatchBuildDone(CodeBlocksEvent& event)
{
    event.Skip();
    /*这次事件不止一次.处理它...*/
    static bool one_time_only = false;
    /*此表达式永远不成立?*/
    if (!m_Batch || one_time_only){return;}
    one_time_only = true;

    cbCompilerPlugin* compiler = static_cast<cbCompilerPlugin*>(event.GetPlugin());
    m_BatchExitCode = compiler->GetExitCode();

    if (m_BatchNotify)
    {
        wxString msg;
        if (m_BatchExitCode == 0) {
            msg << wxT("批构建结束.\n");
        }else {
            msg << wxT("批构建带错误停止.\n");
        }
        msg << wxString::Format(wxT("进程退出带回状态码: %d."), m_BatchExitCode);
        cbMessageBox(msg, appglobals::AppName, m_BatchExitCode == 0 ? wxICON_INFORMATION : wxICON_WARNING, m_pBatchBuildDialog);
    }else{
        wxBell();
    }

    if (m_pBatchBuildDialog && m_BatchWindowAutoClose)
    {
        if (m_pBatchBuildDialog->IsModal()) {
            m_pBatchBuildDialog->EndModal(wxID_OK);
        }else{
            m_pBatchBuildDialog->Destroy();
            m_pBatchBuildDialog = nullptr;
        }
    }
}

/*响应任务栏图标左/下移事件*/
void CodeBlocksApp::OnTBIconLeftDown(wxTaskBarIconEvent& event)
{
    event.Skip();
    if (m_pBatchBuildDialog)
    {
        m_pBatchBuildDialog->Raise();
        m_pBatchBuildDialog->Refresh();
    }
}

/*返回应用的目录*/
wxString CodeBlocksApp::GetAppPath() const
{
    wxString base;
#ifdef __WXMSW__
    wxChar name[MAX_PATH] = {0};
    GetModuleFileName(0L, name, MAX_PATH);
    wxFileName fname(name);
    base = fname.GetPath(wxPATH_GET_VOLUME);
#else
    if (!m_Prefix.IsEmpty()){return m_Prefix;}

#ifdef SELFPATH
    /*SELFPATH is a macro from prefix.h (binreloc) it returns the absolute filename of us similar to win32 GetModuleFileName()...*/
    /*SELFPATH是prefix.h(binreloc)中的一个宏,它返回我们的绝对文件名,类似于win32 GetModuleFileName()...*/
    base = wxString(SELFPATH,wxConvUTF8);
    base = wxFileName(base).GetPath();
#endif
#if defined(sun) || defined(__sun)
    base = wxString(getexecname(),wxConvCurrent);
    base = wxFileName(base).GetPath();
#endif
#if defined(__APPLE__) && defined(__MACH__)
    char path[MAXPATHLEN+1];
    uint32_t path_len = MAXPATHLEN;
    /*SPI first appeared in Mac OS X 10.2*/
    /*SPI最早出现在macosx10.2上*/
    _NSGetExecutablePath(path, &path_len);
    base = wxString(path, wxConvUTF8);
    base = wxFileName(base).GetPath();
#endif
    if (base.IsEmpty())
        base = _T(".");
#endif
    return base;
}

void CodeBlocksApp::SetAutoFile(wxString& file)
{
    m_AutoFile = file;
}

/*命令行解释*/
int CodeBlocksApp::ParseCmdLine(MainFrame* handlerFrame, const wxString& CmdLineString)
{
    /*直接地从控制台wxWindows获取的代码示例 :)*/
    bool filesInCmdLine = false;

#if wxUSE_CMDLINE_PARSER
    wxCmdLineParser& parser = *Manager::GetCmdLineParser();
    if ( CmdLineString.IsEmpty() ) {
        parser.SetCmdLine(argc, argv);
    }else {
        parser.SetCmdLine(CmdLineString);
    }
    /*wxApp::argc参数是一个wxChar**类型*/
    /*不要显示错误,因为插件也有机会解析命令行,所以我们不知道这里到底支持哪些选项*/
    int res = parser.Parse(false);
    if (res == -1){
        return -1;
    }else{
        if (handlerFrame)
        {
            m_HasProject = false;
            m_HasWorkSpace = false;
            int count = parser.GetParamCount();

            parser.Found(_T("file"), &m_AutoFile);

            filesInCmdLine = (count != 0) || (!m_AutoFile.empty());

            for (int param = 0; param < count; ++param)
            {
                /*它是一个项目还是工作区?*/
                FileType ft = FileTypeOf(parser.GetParam(param));
                wxFileName fn(parser.GetParam(param));
                /*非常重要,这样两个同名的文件就不会加载两次*/
                fn.Normalize();
                if (ft == ftCodeBlocksProject)
                {
                    m_HasProject = true;
                    m_DelayedFilesToOpen.Add(parser.GetParam(param));
                }
                else if (ft == ftCodeBlocksWorkspace)
                {
                    /*只能打开一个工作区*/
                    m_HasWorkSpace = true;
                    /*移除其他所有文件*/
                    m_DelayedFilesToOpen.Clear();
                    /*并只添加工作区*/
                    m_DelayedFilesToOpen.Add(fn.GetFullPath());
                    /*并停止处理更多的文件*/
                    break;
                }
                    /*else if (ft == ftSource || ft == ftHeader || ft == ftResource)*/
                else if (wxFile::Exists(fn.GetFullPath()))
                {
                    /*同时尝试打开非源代码文件,头文件和资源文件*/
                    m_DelayedFilesToOpen.Add(fn.GetFullPath());
                }
            }

            /*批处理任务*/
            m_Batch = m_HasProject || m_HasWorkSpace;
            m_Batch = m_Batch && (m_Build || m_ReBuild || m_Clean);
        }
        else
        {
            wxString val;
            /*查找命令行对应的参数*/
            parser.Found(_T("prefix"), &m_Prefix);
            parser.Found(_T("user-data-dir"), &m_UserDataDir);
#ifdef __WXMSW__
            m_DDE = !parser.Found(_T("no-dde"));
            m_Assocs = !parser.Found(_T("no-check-associations"));
#else
            m_DDE = !parser.Found(_T("no-ipc"));
#endif
            m_SafeMode = parser.Found(_T("safe-mode"));
            m_Splash = !parser.Found(_T("no-splash-screen"));
            m_HasDebugLog = parser.Found(_T("debug-log"));
            m_CrashHandler = !parser.Found(_T("no-crash-handler"));
            /*启用日志详情模式*/
            wxLog::EnableLogging(parser.Found(_T("verbose")));

            if (   parser.Found(_T("personality"), &val)
                   || parser.Found(_T("profile"),     &val) )
            {
                SetupPersonality(val);
            }

            /*批处理任务*/
            m_BatchNotify          = parser.Found(_T("batch-build-notify"));
            m_BatchWindowAutoClose = !parser.Found(_T("no-batch-window-close"));
            m_Build                = parser.Found(_T("build"));
            m_ReBuild              = parser.Found(_T("rebuild"));
            m_Clean                = parser.Found(_T("clean"));
            parser.Found(_T("target"), &m_BatchTarget);
            parser.Found(_T("script"), &m_Script);
            /*批处理标志的初始设置(将在再次调用ParseCmdLine()时重置).*/
            m_Batch = m_Build || m_ReBuild || m_Clean;

            if (parser.Found(_T("no-log")) == false){
                Manager::Get()->GetLogManager()->SetLog(new TextCtrlLogger, LogManager::app_log);
            }
            if (parser.Found(_T("log-to-file"))){
                Manager::Get()->GetLogManager()->SetLog(new FileLogger(_T("codeblocks.log")), LogManager::app_log);
            }
            if (m_HasDebugLog) {
                Manager::Get()->GetLogManager()->SetLog(new TextCtrlLogger, LogManager::debug_log);
            }
            if (parser.Found(_T("debug-log-to-file"))) {
                Manager::Get()->GetLogManager()->SetLog(new FileLogger(_T("codeblocks-debug.log")), LogManager::debug_log);
            }
        }

    }
#endif
// wxUSE_CMDLINE_PARSER
    return filesInCmdLine ? 1 : 0;
}

/*个性化设置*/
void CodeBlocksApp::SetupPersonality(const wxString& personality)
{
    if (personality.CmpNoCase(_T("ask")) == 0)
    {
        const wxArrayString items(Manager::Get()->GetPersonalityManager()->GetPersonalitiesList());

        wxSingleChoiceDialog dlg(nullptr, wxT("请选择要加载的个性化配置文件:"),wxT("加载个性化配置文件"),items);

        if (dlg.ShowModal() == wxID_OK)
            Manager::Get()->GetPersonalityManager()->SetPersonality(dlg.GetStringSelection());
    }
    else
        Manager::Get()->GetPersonalityManager()->SetPersonality(personality, true);
}

/*存在要打开文件需要延迟(用编辑器)打开*/
void CodeBlocksApp::LoadDelayedFiles(MainFrame *const frame){

    Manager::Get()->GetLogManager()->DebugLog(wxString::FromUTF8("--22.1加载延迟的文件"));
    std::set<wxString> uniqueFilesToOpen(m_DelayedFilesToOpen.begin(), m_DelayedFilesToOpen.end());
    for (std::set<wxString>::const_iterator it = uniqueFilesToOpen.begin(); it != uniqueFilesToOpen.end(); ++it) {
        frame->Open(*it, true);
    }
    m_DelayedFilesToOpen.Clear();

    /*--file foo.cpp[:line] 带文件名参数格式*/
    if (!m_AutoFile.IsEmpty())
    {
        wxString linePart;
        wxString filePart;
        /*wxString::Last 已被弃用*/
        long linePos = m_AutoFile.Find(_T(':'), true);
        if (linePos != wxNOT_FOUND)
        {
            linePart = m_AutoFile.Mid(linePos + 1, wxString::npos);
            filePart = m_AutoFile;
            filePart.Remove(linePos);
        }

        long line = -1;
        if (linePos != wxNOT_FOUND)
        {
            /*在windows上,如果省略":line":*/
            /*假设ToLong失败时驱动器号在冒号之前*/
            /*c:\foo\bar.h 则给 \foo\bar.h*/
            if ( !linePart.ToLong(&line) )
            {
                /*on windows, if :line is omitted: c:\foo\bar.h -> \foo\bar.h is not the line number!*/
                /*在windows上,如果[:line]被省略:c:\foo\bar.h->\foo\bar.h不是行号!*/
                filePart = m_AutoFile;
            }
        }
        wxFileName fn(filePart);
        /*非常重要,这样两个同名的文件就不会加载两次*/
        fn.Normalize();
        if (frame->Open(fn.GetFullPath(), false))
        {
            EditorBase* eb = Manager::Get()->GetEditorManager()->GetEditor(fn.GetFullPath());
            if (eb && (line != -1)) {
                eb->GotoLine(line - 1, true);
            }
        }
        m_AutoFile.Clear();
    }
}


#ifdef __WXMAC__

void CodeBlocksApp::MacOpenFile(const wxString & fileName )
{
    if (s_Loading)
        m_DelayedFilesToOpen.Add(fileName);
    else if (m_Frame)
        m_Frame->Open(fileName, true);
}

void CodeBlocksApp::MacPrintFile(const wxString & fileName )
{
    wxApp::MacPrintFile(fileName);
}

#endif
// __WXMAC__

/*事件处理*/
/*响应应用激活事件*/
void CodeBlocksApp::OnAppActivate(wxActivateEvent& event)
{
    /*allow others to process this event*/
    /*允许其他人处理此事件*/
    event.Skip();
    /*still loading; we can't possibly be interested for this event ;)*/
    /*仍在加载,我们可能对此事件不感兴趣*/
    if (s_Loading){ return; }

    Manager *manager = Manager::Get();
    /*如果没有管理器或管理器正在关闭,则返回*/
    if (!manager || manager->IsAppShuttingDown()){return;}

    /*Activation & De-Activation event*/
    /*激活和取消激活事件*/

    CodeBlocksEvent cbEvent;
    if (event.GetActive()) {
        cbEvent.SetEventType(cbEVT_APP_ACTIVATED);
    }else{
        cbEvent.SetEventType(cbEVT_APP_DEACTIVATED);
    }

    Manager::Get()->ProcessEvent(cbEvent);

    if (!event.GetActive()){return;}

    /*fix for bug #18007: In batch build mode the following is not needed*/
    /*错误18007的修复:在批生成模式下,不需要以下内容*/
    if (  !m_Batch
          && Manager::Get()->GetEditorManager()
          && Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/check_modified_files"), true))
    {
        // for some reason a mouse up event doesn't make it into scintilla (scintilla bug)
        // therefore the workaround is not to directly call the editorManager, but take a detour through an event
        // the bug is when the file has been offered to reload, no matter what answer you give the mouse is in a selecting mode,
        // adding/removing things to it's selection as you move it around
        // so : idEditorManagerCheckFiles, EditorManager::OnCheckForModifiedFiles just exist for this workaround
        //
        // 由于某种原因,鼠标向上的事件不会使其成为闪烁(闪烁错误),因此解决方法不是直接调用editorManager,
        // 而是绕行一个事件(错误是在文件被提供重新加载时出现的),无论您给出的答案是什么,鼠标都处于选择模式,
        // 在选择时向其添加/删除内容移动它,
        // 以便:idEditorManagerCheckFiles,EditorManager::OnCheckForModifiedFiles只存在于此解决方案中

        wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, idEditorManagerCheckFiles);
        wxPostEvent(Manager::Get()->GetEditorManager(), evt);
        /*项目管理器界面*/
        cbProjectManagerUI *prjManUI = m_Frame->GetProjectManagerUI();
        if (prjManUI){static_cast<ProjectManagerUI*>(prjManUI)->CheckForExternallyModifiedProjects();}
    }

    cbEditor* ed = nullptr;
    /*获取默认内置激活态的编辑器*/
    if(Manager::Get()->GetEditorManager()){ed =Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();}

    if (ed)
    {
        /*hack for linux: without it, the editor loses the caret every second activate o.O*/
        /*hack for linux：没有它,编辑器每秒都会丢失插入符号activate o.o 必须设置焦点激活*/
        Manager::Get()->GetEditorManager()->GetNotebook()->SetFocus();
        ed->GetControl()->SetFocus();
    }
}

void CodeBlocksApp::AddFileToOpenDelayed(const wxString& filename)
{
    m_DelayedFilesToOpen.Add(filename);
}